#include "includes.h"
#include "structs.h"
#include "declares.h"

/* this section discusses the Fragment:
 * 
 * o: since our graph is undirected
 *  isBorderVertexWithOutgoingEdge(u) -> number of fids that u communicates to > 0
 *  this can be a member of struct vertex_t as each vertex has at least 0 fids that 
 *  communicates to.
 * o: isInnerVertex (u) -> fragtable[u] == self.fid
 * o: getInnerVertex() -> an igraph_vector_ptr_t of vertex_t*
 * o: get all the fids it communicates to: visits v->fids if v->nfid > 0
 * o: getOutgoingEdge(u) -> visits the adjlist of u 
 */

/* load_fragment: Creates N_FRAG fragments 
 * and a global adjlist from vertices,
 * edges and fragtable 
 * Time: O(vn+en+N_FRAG)
 */

int load_fragment(
                int nfrag,
                igraph_vector_ptr_t *vertices,
                igraph_vector_ptr_t *edges,
                igraph_vector_int_t *fragtable,
                my_adjlist_t *global_adjlist,
                struct Fragment *fragments
                )
{
        int vn=igraph_vector_ptr_size(vertices);
        int en=igraph_vector_ptr_size(edges);
        struct vertex_t *pv;
        struct edge_t *pe;
        int vid,belongs,fid;
        int src, dst;
        igraph_vector_ptr_t* pf;
 
        // init fragments 
        for(int i=0;i<nfrag;++i) {
                fragments[i].innerv=xmalloc(sizeof(igraph_vector_ptr_t));
                fragments[i].fid=i;
                igraph_vector_ptr_init( fragments[i].innerv , 0);
                fragments[i].innerv_it=0;
                fragments[i].local_adjlist=xmalloc(sizeof(my_adjlist_t));
        }

        // visits vertices and build innerv
        for(int i=0;i<vn;++i) {
                /* pv = VECTOR(*vertices)[i]; */
                pv=igraph_vector_ptr_e(vertices,i);
                vid=pv->vid;
                /* belongs=VECTOR(*fragtable)[vid]; */
                belongs=igraph_vector_int_e(fragtable,vid);
                pv->fid=belongs;
                pf=fragments[belongs].innerv;
                pv->locid=igraph_vector_ptr_size(pf);
                igraph_vector_ptr_push_back(pf, pv);
        }
        


        // build local adjlist 
        for(int i=0;i<nfrag;++i) {
                int innerv_size= igraph_vector_ptr_size(fragments[i].innerv);
                fragments[i].innerv_size=innerv_size;
                my_adjlist_init_empty(fragments[i].local_adjlist, innerv_size); 
                fragments[i].global_adjlist=global_adjlist;
        }
        
        // build local adjlist for each fragment 
        // for each v, filter out the u that is not in the same fragment with him
        // in his adjlist 
        for(int i=0;i<vn;++i) {
                igraph_vector_ptr_t *each_gadjlist=my_adjlist_get(global_adjlist,i); // global_adjlist to be scaned
                int size=igraph_vector_ptr_size(each_gadjlist);
                pv=igraph_vector_ptr_e(vertices,i); 
                int src_fid=pv->fid;
                int local_vid=pv->locid;
                igraph_vector_ptr_t *each_ladjlist=my_adjlist_get( fragments[src_fid].local_adjlist, local_vid);
                // local_adjlist to be filled
                for(int j=0;j<size;++j) {
                        struct edge_t *pe, *local_pe;
                        pe=igraph_vector_ptr_e(each_gadjlist,j); // fetch an edge from global_adjlist
                        int dst=pe->dst;
                        struct vertex_t *pv_dst = igraph_vector_ptr_e(vertices, dst);
                        int dst_fid=pv_dst->fid;
                        if(dst_fid != src_fid)
                                continue;
                        // not within the same fragment
                        
                        local_pe=make_edge(local_vid,pv_dst->locid,pe->attr,pe->eid);
                        igraph_vector_ptr_push_back(each_ladjlist,local_pe);
                }
        }

        // visits edges and build nfid and fids of vertex_t (communication list) 
        // use an igraph_matrix_bool_t to mark if a fid 
        // has been add to a vertex's communication list
        
        igraph_matrix_bool_t hasadd;
        igraph_matrix_bool_init(&hasadd,vn,nfrag);
        igraph_bool_t *pbool;
        for(int i=0;i<en;++i) {
                /* pe=VECTOR(*edges)[i]; */
                pe=igraph_vector_ptr_e(edges,i);
                src=pe->src;
                dst=pe->dst;
                int srcbelong=VECTOR(*fragtable)[src];
                int dstbelong=VECTOR(*fragtable)[dst];
                if(srcbelong == dstbelong)
                        continue; // they are not border vertices

                // add to v's fids list
                pbool=igraph_matrix_bool_e_ptr(&hasadd,src,dstbelong);
                if(! *pbool) {
                        *pbool = 1;
                        /* pv=VECTOR(*vertices)[src]; */
                        pv=igraph_vector_ptr_e(vertices,src);
                        igraph_vector_int_push_back(pv->fids, dstbelong);
                }

                pbool=igraph_matrix_bool_e_ptr(&hasadd, dst,srcbelong);
                if(! *pbool) {
                        *pbool=1;
                        /* pv=VECTOR(*vertices)[dst]; */
                        pv=igraph_vector_ptr_e(vertices,dst);
                        igraph_vector_int_push_back(pv->fids, srcbelong);
                }
        }

        printl_fragments(fragments, nfrag);
        igraph_matrix_bool_destroy(&hasadd);
        return 0;
} 

void fragment_free(struct Fragment *fragment)
{
        // who Creates it does the cleanup
        // only free the refs to the global vertices
        // so not register a destructor for innerv
        igraph_vector_ptr_destroy(fragment->innerv);
        my_adjlist_destory(fragment->local_adjlist);
}



void print_fragments(FILE *s, struct Fragment *fragments, int nfrag)
{
        struct vertex_t *pv;
        fprintf(s,"fragments(nfrag=%d\n\n",nfrag);
        fputs("-----------------------------------------------------------------------------------\n", s);
        for(int i=0;i<nfrag;++i) {
                fprintf(s,"\tfragment(fid=%d)\n\n\tlocal_adjlist=\n", fragments[i].fid);
                int size=igraph_vector_ptr_size((fragments[i].innerv));
                fputs("\t\t", s);
                my_adjlist_print(s,fragments[i].local_adjlist);
                fprintf(s, "\tinner_vertices=\n");
                for(int j=0;j<size;++j) {
                        fputs("\t\t", s);
                        /* struct vertex_t *pv=VECTOR(*(fragments[i].innerv))[j]; */
                        pv=igraph_vector_ptr_e(fragments[i].innerv, j);
                        print_vertex(s,pv);
                }
                fputs("-----------------------------------------------------------------------------------\n", s);
        }
        fputs(")\n", s);
}

/* for convinience, code a iterator struct for visiting 
 * innerv of a fragment
 */

/* use it like this:
 * 
 * initInnerVertex(fragment);
 * 
 * while(!endInnerVertex(fragment))
 * {
 *      struct vertex_t *pv=getInnerVertex(fragment);
 * }
 * 
 * in an iterator manner
 */
void initInnerVertex(struct Fragment *fragment)
{
        fragment->innerv_it=0;
}

struct vertex_t *getInnerVertex(struct Fragment *fragment)
{

        int index=fragment->innerv_it;
        fragment->innerv_it++;
        return fragment->innerv->stor_begin[index];
}


int endInnerVertex(struct Fragment *fragment)
{
        return fragment->innerv_it == igraph_vector_ptr_size(fragment->innerv);
}

/* check if vid belongs to fid, that is, isInnerVertex of fid */
/* NO CHECK */

/* since we maintain the communication list in each vertex, this function */
/* becomes useless */
int isInnerVertex(struct Fragment *fragment, struct vertex_t *pv)
{
        return pv->fid == fragment->fid;
}
